梦入琼楼寒有月,行过石树冻无烟

Spring Boot 读取与自动配置

读取

Environment

Environment是用于读取应用程序运行时环境变量的类,是可通过在application.properties中写入的key来通过Environment类读取的一种方式,而通过这种方式来读取的实现类通常称之为“Environment”类。

src/main/resources/application.properties

1
demo.msg = Hello I am here read config!

HelloWorldController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
@Autowired
private Environment env;
@RequestMapping("/demoEnv")
public String demoEnv() {
// 从 src/main/resources/application.properties 中的key读取配置文件数据
return "Spring boot:" + env.getProperty("demo.msg");
}
}

@Value

第二种方法就是使用@value注解来进行读取,与Environment的区别就是少了一行code,就是说用注解代替了Environment的return,来读取application.properties:

src/main/resources/application.properties

1
demo.msg = Hello I am here read config!

HelloWorldController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
@Value("${demo.msg}")
private String msg;
@RequestMapping("/demoValue")
public String demoValue() {
return "Spring boot" + msg;
}
}

@PropertySource

在前面所介绍的注解仅仅可用于读取application.properties的Spring boot全局配置文件,而如果想要读取另一个或其他的配置文件需要使用到@PropertySource + @Value来读取其他配置文件的内容,其步骤如下:

PropertySourceValueReaderOtherController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@PropertySource({"one.properties","two.properties"})
public class PropertySourceValueReaderOhterController {
@Value("${two.msg}")
private String twomsg;

@Value("${one.msg}")
private String onemsg;

@RequestMapping("/demoProperty")
public String demoProperty() {
return "one.properties:" + twomsg + "<br>" +
"two.properties:" + twomsg;
}

}

one.properties

1
one.msg = Hello

two.properties

1
two.msg = test PropertySource

@ConfigurationProperties

```@Value、@PropertySource```注解类似,通过使用该注解可以配置 Controller、Model 进行获取 ```application.properties``` 配置文件信息。
1
2
3
4
5
6
7
8

#### application.properties
```java
com.example.name=${name:sunlikun}
com.exmaple.age=16
com.example.address[0]=one
com.example.address[1]=two
com.example.address[2]=three

ConfigExampleModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.example.demo.model;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* ConfigExampleModel class
*
* @author sunlikun
* @date 2021/06/08
*/
@Data
@Component
@ConfigurationProperties(prefix = "com.example")
public class ConfigExampleModel {
/**
* @Data 自动生成 setter()、getter()、toString()、equals()、hashCode() 方法;
* @Component @Component 及表示为当时用该注解时,Spring 框架将会自动检测到这些类进行注入。通常表示说当前类不属于各种归类
* 时(如 Controller、Services)就可以通过使用该注解进行标注。
* @ConfigurationProperties 将配置信息自动封装为实体类,其 "prefix(前缀)"是配置文件中定义的配置信息: "com.example.{value}"
*/

private String name;
private int age;
private List<String> address;
}

ConfigExampleController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.demo.controller;

import com.example.demo.model.ConfigExampleModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* ConfigExampleController class
*
* @author kunlun
* @date 2021/06/08
*/
@RestController
public class ConfigExampleController {
@Autowired
private ConfigExampleModel configExampleModel;

@RequestMapping("/")
public String getName() {
return configExampleModel.getName();
}

@RequestMapping("/address")
public List<String> getAddress() {
return configExampleModel.getAddress();
}
}

当一切完成之后我们访问 http://localhost:8080/address 会发现返回的是 ["one","two","three"],或者访问 http://localhost:8080/ 进行测试,此时返回的结果则是 sunlikun,而这正是 application.properties 下配置文件的内容数据。

日志

在众多的开发语言之中,日志是最为重要的一个功能,虽然这里说的开发语言不指java、c\c++、php这些,但是我们可拿mysql为例,在mysql中,日志是非常好的排错工具,通过阅读日志信息可帮助我们快速的了解错误和安全性的信息,学习spring boot自动配置之前,我们需要预先连接下spring boot的日志生成和管理以及对日志的配置。通常我们使用apache下的Log、LogFactory的方式来实现日志,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.demo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LogTestController {
private Log log = LogFactory.getLog(LogTestController.class);
@RequestMapping("/testLog")
public String testLog() {
log.info("test log");
return "test Log";
}
}

日志等级

在Spring boot中,日志等级和一些日志配置都是在application.properties文件下进行设置的,通常存储在src/main/resources目录下,而日志等级主要分为五个等级级别,以及三个颜色等级,共分别为:

ID DA FA
OFF 关闭 无颜色
FATAL 致命 红色
ERROR 错误 红色
WARN 警告 黄色
INFO 信息 绿色
DEBUG 调试 绿色
TREACE 追踪 绿色
ALL 所有 无颜色
通常我们可以使用针对默认和设置指定包下的日志等级两种方式为:

默认日志等级设置

1
2
# 设置日志的默认等级为 info
logging.level.root = info

指定包下的日志等级设置

1
2
# 设置 com.example.demo 包下日志等级为 off 即关闭
logging.level.com.example.demo = off

日志目录

在spring boot全局配置文件中,默认是在启动时输出日志文件,而不会针对性的单个生成一个日志文件,这就显得单个生成日志文件存储是多么重要,通常我们可以在spring boot全局配置文件中的application.properties文件中设置:

1
logging.file = spring.log

以上是针对于spring.log是存放于spring项目目录下的,如果运行后spinrg boot发现日志文件并没有写入则是可能spring boot未找到spring.log日志文件,而遇到这种事情 spring boot提供了一个完美的解决方案就是通过使用相对目录进行指定:

1
logging.file.name=/home/kunlun/Development/Web/Demo/Spring/demo/src/main/resources/spring.log

在spring boot logging日志中有一个非常好的功能呢个就是,如果当日志文件超过了10mb时,将会自动生成一个新的日志文件。与此同时spring boot提供了日志的自定义,如自定义错误的颜色以及时间、等级、长度、方法名、代码行等自定义,有兴趣的读者可通过查看spring boot 关于日志这一篇的文档:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-logging

自动配置原理

在spring boot中,@SpringBootApplication注解之中,主要调用并使用了@EnableAutoConfiguration注解来进行标注,从而实现spring boot的自动配置功能,来帮助开发者解决spirng原生的蛋疼配置问题,我们可以通过阅读spring boot的@EnableAutoCOnfiguratiot的官方Java Doc的官方文档和其依赖的源代码查看并理解其Spirng boot自动配置的原理。

EnableAutoConfiguration.class

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 排除特定的自动配置类,使它们永远不会被应用。
* @return the classes to exclude
*/
Class<?>[] exclude() default {};

/**
* 排除特定的自动配置类名,这样它们就永远不会被应用
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};

通常只要是一个标准的Spring boot代码都会使用 @SpringBootApplication注解,而通过阅读@SpringBootApplication对应的代码我们发现了@SpringBootApplication注解包含了@EnableAutoOnfiguration注解,而进一步阅读源代码后发现其在Springboot启动的时候会夹在主配置类AutoConfigurationImportSelector.class

AutoConfigurationImportSelector.class

1
2
3
4
5
6
7
8
9
10
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断@EnableAutoOnfiguration 注解是否开启,如没有开启则返回,默认开启(不然他也不会写!isEnable)
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 自动配置条目
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
1
2
3
4
   // SpringFactoriesLoader从META-INF/spring中夹在实例化给指定的工厂,而这些可能存在于类中的路径中的多个jar文件中
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
1
2
3
4
5
6
7
8
9
10
11
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

// 删除重复的配置 remoe Duplicates
configurations = removeDuplicates(configurations);

}

从上述代码我们可以大概的了解到Spring Boot是通过加载所有的jar文件来实现自动配置的,所以@SpringBootApplication是通过使用@EnableAutoConfiguration注解自动配置的原理即从class目录中搜索所有的spring.factories配置文件并将其中EnableAutoConfiguration对应的配置项通过java反射机制进行实例化之后夹在到spring ioc容器中。

⬅️ Go back